package com.framework.workflow.service.impl;

import com.framework.common.config.SessionUtil;
import com.framework.common.util.SpringUtils;
import com.framework.workflow.constant.DataDict;
import com.framework.workflow.dto.NodeProperty;
import com.framework.workflow.dto.NodeSettingDto;
import com.framework.workflow.dto.NodeUser;
import com.framework.workflow.model.WfProcessInstance;
import com.framework.workflow.params.NodeSettingParam;
import com.framework.workflow.service.IPropertyService;
import com.framework.workflow.strategy.AssigneeFilterContext;
import com.framework.workflow.strategy.IAssigneeFilterStrategy;
import com.framework.workflow.util.WorkflowHelper;
import com.framework.workflow.vo.NodeSetting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.ExtensionAttribute;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author zzg
 * @date 2025-6-9 13:37
 */
@Slf4j
@Service
public class PropertyService implements IPropertyService {
    @Autowired
    private ProcessInstanceService processInstanceService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private VariableService variableService;
    @Autowired
    private TaskService taskService;
    @Autowired
    protected ProcessEngine processEngine;

    /**
     * 获取流程图上节点的配置（原始属性）
     * @param processDefinitionId 流程定义编号
     * @param taskDefKey 当前节点，为空默认获取第一个节点
     * @param variables 参数
     * @return 节点配置信息
     */
    @Override
    public NodeProperty getNextNodeProperty(String processDefinitionId, String taskDefKey, Map<String, Object> variables) {
        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
        Process process = bpmnModel.getMainProcess();
        return WorkflowHelper.next(process, taskDefKey, variables);
    }

    /**
     * 获取流程节点处理信息
     * 用于前端流程处理弹窗页面显示
     * @param param 任务编号，业务编号
     * @return 节点配置信息
     */
    @Override
    public NodeSetting getNodeSetting(NodeSettingParam param) {
        NodeSetting nodeSetting = new NodeSetting();
        NodeSettingDto dto = getParam(param);
        nodeSetting.setStatus(dto.getStatus());
        nodeSetting.setProcInstId(dto.getProcInstId());

        // 获取流程参数
        String procInstId = dto.getProcInstId();
        HashMap<String, Object> variables = variableService.getVariable(procInstId, dto.getTaskId());
        if(null != param.getAttribute()){
            variables.putAll(param.getAttribute());
        }

        String procDefId = dto.getProcDefId();
        // 流程未启动
        if (StringUtils.isEmpty(procDefId)) {
            ProcessDefinition processDefinition = getProcess(dto.getProcDefKey());
            procDefId = processDefinition.getId();
        }

        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(procDefId);
        NodeProperty nodeProperty = getNextNodeProperty(dto.getProcDefKey(), dto.getTaskDefKey(), variables);
        nodeSetting.setNodeProperty(nodeProperty);
        List<String> groups = nodeProperty.getGroups();
        if (null != groups && groups.size() > 0) {
            // 根据角色取用户
            List<NodeUser> userList = variableService.userNodesByRole(groups);
            nodeSetting.setNextUserList(userList);
        }

        // 需要按策略过滤,下一步处理人
        String strategyFilter = nodeProperty.getStrategy();
        List<NodeUser> nextUserList = nodeSetting.getNextUserList();
        if(!SessionUtil.isAdmin() && StringUtils.isNotEmpty(strategyFilter) && CollectionUtils.isNotEmpty(nextUserList)){
            IAssigneeFilterStrategy strategy = SpringUtils.getBean(strategyFilter);
            if (ObjectUtils.isNotEmpty(strategy)) {
                List<String> users = nextUserList.stream().map(NodeUser::getId).collect(Collectors.toList());
                AssigneeFilterContext context = new AssigneeFilterContext(strategy);
                context.filter(users, variables, nodeProperty);
                nextUserList = nextUserList.stream().filter(item -> users.contains(item.getId())).collect(Collectors.toList());
                nodeSetting.setNextUserList(nextUserList);
            } else {
                log.error("沒有配置角色的过滤策略");
            }
        }

        List<String> users = nodeProperty.getUsers();
        if (null == nodeSetting.getNextUserList() && null != users && users.size() > 0) {
            List<NodeUser> userList = variableService.userNodeByIds(users);
            nodeSetting.setNextUserList(userList);
        }

        UserTask element = (UserTask) bpmnModel.getFlowElement(dto.getTaskDefKey());
        Map<String, List<ExtensionAttribute>> fileMap =  element.getAttributes();
        // 是否有人员选择下拉框
        String dueAuto = WorkflowHelper.getExtendAttribute(fileMap, "dueAuto");
        // 是否显示处理意见
        String showDescription = WorkflowHelper.getExtendAttribute(fileMap, "showDescription");
        if(!nodeProperty.isEnd()){
            nodeSetting.setDueAuto(dueAuto);
        }
        nodeSetting.setShowDescription(showDescription);
        return nodeSetting;
    }

    @Override
    public Process getMainProcess(String processDefinitionId) {
        BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);
        return bpmnModel.getMainProcess();
    }

    private NodeSettingDto getParam(NodeSettingParam param) {
        NodeSettingDto dto = new NodeSettingDto();
        String businessId = param.getBusinessId();
        if (StringUtils.isNotBlank(param.getTaskId())) {
            Task task = taskService.createTaskQuery().taskId(param.getTaskId()).singleResult();
            dto.setProcInstId(task.getProcessInstanceId());
            dto.setProcDefId(task.getProcessDefinitionId());
            dto.setTaskDefKey(task.getTaskDefinitionKey());
            dto.setStatus(DataDict.WfInsStatus.RUNNING.toString());
            return dto;
        }

        if (StringUtils.isBlank(businessId)) {
            dto.setStatus(DataDict.WfInsStatus.TO_START.toString());
            return dto;
        }

        // 已存在业务id， 有可能是保存，也有可能上报
        WfProcessInstance instance = processInstanceService.findByBusinessId(businessId);
        // 如果为空，表示还没发起流程, 返回初始阶段
        if (null == instance) {
            dto.setStatus(DataDict.WfInsStatus.TO_START.toString());
            return dto;
        }

        // 如果有实例， 就能拿到流程参数
        String procInstId = instance.getProcInstId();
        dto.setProcInstId(procInstId);
        dto.setProcDefId(instance.getProcDefId());
        // 流程未结束存在任务
        Task task = null;
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(procInstId).list();
        if (tasks.size() == 0) {
            dto.setStatus(DataDict.WfInsStatus.END.toString());
        } else {
            dto.setStatus(DataDict.WfInsStatus.RUNNING.toString());
        }
        if (tasks.size() > 1) {
            String userId = SessionUtil.getUser().getId();
            if (StringUtils.isNotBlank(userId)) {
                Optional<Task> optional = tasks.stream().filter(it -> it.getAssignee().equals(userId)).findFirst();
                if (optional.isPresent()) {
                    task = optional.get();
                }
            }
        }
        if (null == task) {
            task = tasks.get(0);
        } else {
            dto.setTaskId(task.getId());
        }
        String taskDefKey = task.getTaskDefinitionKey();
        dto.setTaskDefKey(taskDefKey);

        return dto;
    }



    public ProcessDefinition getProcess(String processDefinitionKey) {
        return processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .active()
                .latestVersion()
                .singleResult();
    }
}
